package com.erlang4j.internal;

import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangPid;
import com.erlang4j.api.Erlang4jMessageLanguage;
import com.erlang4j.api.IBinding;
import com.erlang4j.api.IErlang4j;
import com.erlang4j.api.exceptionHandler.IExceptionHandler;
import com.erlang4j.api.process.IProcess;
import com.erlang4j.internal.basicIo.IBasicMailBox;
import com.erlang4j.internal.basicIo.IBasicMailBoxFactory;
import com.erlang4j.internal.basicIo.MailBox;
import com.erlang4j.internal.basicIo.OtpNodeBasicOperationsFactory;
import com.erlang4j.internal.messages.IMessageHandler;
import com.erlang4j.internal.messages.MessageComposer;
import com.erlang4j.internal.messages.MessageParser;
import com.erlang4j.internal.process.StateMachine;
import com.erlang4j.internal.stateMachine.IStateDefinition;
import com.erlang4j.internal.stateMachine.StateDefinition;

/**
 * <p>
 * This is the concrete implementation of your IErlang4J code. This is the only class that you should ever create using
 * "new", and even that should be only once
 * </p>
 * 
 * @author Phil Rice
 */
public class Erlang4J implements IErlang4j {

	private final IBasicMailBoxFactory factory;
	private final MailBox mailBox;

	private final IExceptionHandler exceptionHandler;
	private final MessageParser parser = new MessageParser();
	private final MessageComposer composer;

	public Erlang4J(String erlangVm, String cookie, String javaNodeName, IExceptionHandler exceptionHandler) {
		this(new OtpNodeBasicOperationsFactory(erlangVm, cookie, javaNodeName), exceptionHandler);
	}

	public Erlang4J(IBasicMailBoxFactory factory, IExceptionHandler handler) {
		this.exceptionHandler = handler;
		this.factory = factory;
		this.mailBox = new MailBox(factory.operations());
		this.composer = new MessageComposer(makeDefaultBindings(self()));
	}

	private IBinding makeDefaultBindings(OtpErlangPid self) {
		return Erlang4jMessageLanguage.binding("Self", self);
	}

	@Override
	public IExceptionHandler exceptionHandler() {
		return exceptionHandler;
	}

	@Override
	public IProcess spawn(IMessageHandler... handlers) {
		IBasicMailBox newOperations = factory.operations();
		StateDefinition stateDefinition = new StateDefinition(null, handlers);
		StateMachine erlang4JProcess = new StateMachine(newOperations, exceptionHandler, makeDefaultBindings(newOperations.self()), stateDefinition);
		if (handlers.length > 0)
			erlang4JProcess.spawn();
		return erlang4JProcess;
	}

	public IProcess spawnStateMachine(IStateDefinition... stateDefinitions) {
		IBasicMailBox newOperations = factory.operations();
		return new StateMachine(newOperations, exceptionHandler, makeDefaultBindings(newOperations.self()), stateDefinitions);
	}

	// ========================= Delegates below here ================

	public IBinding parse(OtpErlangObject object, String input) {
		return parser.parse(object, input);
	}

	public IBinding parse(OtpErlangObject object, String input, IBinding binding) {
		return parser.parse(object, input, binding);
	}

	public OtpErlangObject compose(String input, Object... parameters) {
		return composer.compose(input, parameters);
	}

	public OtpErlangObject composeFromBinding(String input, IBinding binding) {
		return composer.composeFromBinding(input, binding);
	}

	public OtpErlangObject receive() {
		return mailBox.receive();
	}

	public OtpErlangObject rpc(String serverNameOrPid, String name, Object object) {
		return mailBox.rpc(serverNameOrPid, name, object);
	}

	public OtpErlangPid self() {
		return mailBox.self();
	}

	public void sendRaw(Object serverNameOrPid, Object object) {
		mailBox.sendRaw(serverNameOrPid, object);
	}

	public void send(Object serverNameOrPid, String pattern, Object... parameters) {
		mailBox.send(serverNameOrPid, pattern, parameters);
	}

	public void sendWithBinding(Object serverNameOrPid, String pattern, IBinding binding) {
		mailBox.sendWithBinding(serverNameOrPid, pattern, binding);
	}

	@Override
	public void sendRpc(Object serverNameOrPid, String atom, Object... parameters) {
		mailBox.sendRpc(serverNameOrPid, atom,parameters);
		
	}

}
